﻿using System.Diagnostics.CodeAnalysis;

using zijian666.WorkScheduler.Abstractions;
using zijian666.WorkScheduler.Defines;

namespace zijian666.WorkScheduler.Defaults;

/// <summary>
/// 表示一个工作上下文。
/// </summary>
public class WorkContext : DisposableBase, IWorkContext, IDisposable
{
    private readonly IWorkManager _manager;
    private readonly IToken _token;

    /// <param name="manager"> 工作管理群。 </param>
    /// <param name="token"> 执行令牌。 </param>
    /// <param name="data"> 工作数据。 </param>
    public WorkContext(IWorkManager manager, IToken token, WorkData data)
    {
        _manager = manager ?? throw new ArgumentNullException(nameof(manager));
        _token = token ?? throw new ArgumentNullException(nameof(token));
        _token.Cancellation.Register(() => this.TryDispose());
        _token.Cancellation.ThrowIfCancellationRequested();
        if (data.IsEmpty)
        {
            throw new ArgumentNullException(nameof(data));
        }
        Data = data;
    }

    public string Ticket => Data.Ticket!;

    public WorkData Data
    {
        get;
        internal set
        {
            _cts.ThrowIfCancellationRequested($"工作 {Data.Id} 当前状态：{Data.Status}。");
            if (value.IsEmpty)
            {
                this.TryDispose();
                return;
            }
            field = value;
            if (value.Status.IsCompleted())
            {
                this.TryDispose();
            }
        }
    }

    /// <summary>
    /// 工作完成
    /// </summary>
    /// <param name="result">工作结果</param>
    /// <param name="remark">备注</param>
    /// <returns></returns>
    public async Task Success(string result, string? remark)
    {
        result.ThrowIfNull(nameof(result));
        ThrowIfDisposed();
        var progress = new WorkProgress(Data)
        {
            CurrentStatus = WorkStatus.Success,
            Progress = 1,
            Result = result,
            Remark = remark,
        };
        _token.Activation();
        var work = await _manager.Report(progress);
        Data = work;

    }

    /// <summary>
    /// 上报工作进度。
    /// </summary>
    /// <param name="progressValue">工作进度</param>
    /// <param name="remark">备注</param>
    /// <exception cref="OperationCanceledException">当前工作状态不等于 <seealso cref="WorkStatus.Running"/></exception>
    public async Task Report(double progressValue, string? remark)
    {
        progressValue = Math.Max(0, Math.Min(1, progressValue));
        ThrowIfDisposed();
        _token.Activation();
        var progress = new WorkProgress(Data)
        {
            CurrentStatus = WorkStatus.Running,
            Progress = progressValue,
            Remark = remark,
        };
        var work = await _manager.Report(progress);
        Data = work;
        if (work.IsEmpty)
        {
            throw new InvalidOperationException("工作数据丢失。");
        }
    }

    /// <summary>
    /// 工作失败
    /// </summary>
    /// <param name="exception">异常</param>
    /// <param name="remark">备注</param>
    /// <param name="progressValue">工作进度</param>
    /// <exception cref="ArgumentNullException">异常参数不能为空</exception>
    /// <exception cref="InvalidOperationException">进度小于0或大于1</exception>
    /// <exception cref="InvalidOperationException">当前工作状态不等于 <seealso cref="WorkStatus.Running"/></exception>
    public async Task Fail(Exception exception, string? remark = null, double progressValue = 1)
    {
        exception.ThrowIfNull(nameof(exception));
        progressValue = Math.Max(0, Math.Min(1, progressValue));
        ThrowIfDisposed();
        var progress = new WorkProgress(Data)
        {
            CurrentStatus = WorkStatus.Error,
            Progress = progressValue,
            Exception = exception.GetBaseException(),
        };
        _token.Activation();
        var work = await _manager.Report(progress);
        Data = work;
        this.TryDispose();
    }
}
